package hmvictor.jpql;

import hmvictor.jpql.renderers.ParameterTypeRenderer;
import hmvictor.jpql.types.BooleanEditor;
import hmvictor.jpql.types.CharacterEditor;
import hmvictor.jpql.types.ComposedType;
import hmvictor.jpql.types.CalendarTemporalDate;
import hmvictor.jpql.types.DateTemporalDate;
import hmvictor.jpql.types.DefaultType;
import hmvictor.jpql.types.Editor;
import hmvictor.jpql.types.InvalidEditorValueException;
import hmvictor.jpql.types.ParameterType;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Frame;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.Parameter;
import javax.persistence.Query;
import javax.swing.DefaultComboBoxModel;
import javax.swing.GroupLayout;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

/**
 *
 * @author Victor
 */
public class ParameterDialog extends javax.swing.JDialog {

    private List<Parameter<?>> parameters;
    private List<JLabel> labels;
    private List<JComboBox<Class>> combos;
    private List<JPanel> panels;
    private List<JLabel> errorLabels;

    private final List<ParameterType> types = new LinkedList<>();

    private final Map<Parameter, Editor> editorsByParameter = new HashMap<>();

    private Query query;

    private boolean canceled;

    private ParameterCache parameterCache;

    /**
     * Creates new form ParameterDialog
     */
    public ParameterDialog(java.awt.Frame parent, boolean modal) {
        super(parent, modal);
        initComponents();
        types.add(new DefaultType("String", (string) -> string));
        types.add(new DefaultType("Byte", (string) -> Byte.valueOf(string)));
        types.add(new DefaultType("Short", (string) -> Short.valueOf(string)));
        types.add(new DefaultType("Integer", (string) -> Integer.valueOf(string)));
        types.add(new DefaultType("Long", (string) -> Long.valueOf(string)));
        types.add(new DefaultType("Float", (string) -> Float.valueOf(string)));
        types.add(new DefaultType("Double", (string) -> Double.valueOf(string)));
        types.add(new DefaultType("BigInteger", (string) -> new BigInteger(string)));
        types.add(new DefaultType("BigDecimal", (string) -> new BigDecimal(string)));
        types.add(new ComposedType("Boolean", BooleanEditor.class));
        types.add(new ComposedType("Character", CharacterEditor.class));
        types.add(new CalendarTemporalDate());
        types.add(new DateTemporalDate());
    }

    public void setQuery(Query query) {
        this.query = query;
    }

    public void setParameterCache(ParameterCache parameterCache) {
        this.parameterCache = parameterCache;
    }

    public void setParameters(Set<Parameter<?>> parameters) {
        labels = new LinkedList<>();
        combos = new LinkedList<>();
        panels = new LinkedList<>();
        errorLabels=new LinkedList<>();
        this.parameters = new LinkedList<>(parameters);
        for (Parameter parameter : parameters) {
            JLabel jLabel = new JLabel();
            if (parameter.getName() == null) {
                jLabel.setText(String.valueOf(parameter.getPosition()));
            } else {
                jLabel.setText(parameter.getName());
            }
            labels.add(jLabel);
            final JComboBox combo = new JComboBox();
            DefaultComboBoxModel m = new DefaultComboBoxModel();
            for (ParameterType type : types) {
                m.addElement(type);
            }
            combo.setModel(m);
            combo.setRenderer(new ParameterTypeRenderer());
            combo.setSelectedIndex(-1);
            combos.add(combo);
            final JPanel panel = new JPanel();
            panel.setLayout(new BorderLayout());
            panels.add(panel);
            combo.addActionListener((event) -> {
                ParameterType type = (ParameterType) combo.getSelectedItem();
                Editor editor = type.createEditor();
                JComponent component = null;
                if (parameter.getName() != null) {
                    if (parameterCache.contains(parameter.getName()) && type.equals(parameterCache.get(parameter.getName()).getType())) {
                        component = editor.getComponent(parameterCache.get(parameter.getName()).getValue());
                    }
                } else {
                    if (parameterCache.contains(parameter.getPosition()) && type.equals(parameterCache.get(parameter.getName()).getType())) {
                        component = editor.getComponent(parameterCache.get(parameter.getPosition()).getValue());
                    }
                }
                if (component == null) {
                    component = editor.getComponent();
                }
                panel.removeAll();
                panel.add(component);
                parametersPanel.revalidate();
                editorsByParameter.put(parameter, editor);
            });
            if (parameter.getName() != null) {
                if (parameterCache.contains(parameter.getName())) {
                    combo.setSelectedItem(parameterCache.get(parameter.getName()).getType());
                }
            } else {
                if (parameterCache.contains(parameter.getPosition())) {
                    combo.setSelectedItem(parameterCache.get(parameter.getPosition()).getType());
                }
            }
            combo.setSelectedIndex(0);
            JLabel errorLabel = new JLabel();
            errorLabel.setForeground(Color.RED);
            errorLabels.add(errorLabel);
        }
        GroupLayout groupLayout = new GroupLayout(parametersPanel);
        groupLayout.setAutoCreateGaps(true);
        groupLayout.setAutoCreateContainerGaps(true);
        GroupLayout.ParallelGroup labelsGroup = groupLayout.createParallelGroup();
        GroupLayout.ParallelGroup combosGroup = groupLayout.createParallelGroup();
        GroupLayout.ParallelGroup panelsGroup = groupLayout.createParallelGroup();
        for (int i = 0; i < labels.size(); i++) {
            labelsGroup.addComponent(labels.get(i));
            combosGroup.addComponent(combos.get(i));
            panelsGroup.addComponent(panels.get(i));
            panelsGroup.addComponent(errorLabels.get(i));
        }
        parametersPanel.setLayout(groupLayout);
        groupLayout.setHorizontalGroup(
                groupLayout.createSequentialGroup()
                .addGroup(labelsGroup).addGroup(combosGroup).addGroup(panelsGroup));
        GroupLayout.SequentialGroup rowsGroup = groupLayout.createSequentialGroup();
        groupLayout.setVerticalGroup(rowsGroup);
        for (int i = 0; i < labels.size(); i++) {
            rowsGroup.addGroup(groupLayout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(labels.get(i), GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                    .addComponent(combos.get(i), GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                    .addComponent(panels.get(i), GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE));
            rowsGroup.addComponent(errorLabels.get(i));
        }
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jButton1 = new javax.swing.JButton();
        okButton = new javax.swing.JButton();
        parametersPanel = new javax.swing.JPanel();
        errorMessage = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setTitle(org.openide.util.NbBundle.getMessage(ParameterDialog.class, "ParameterDialog.title")); // NOI18N
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(ParameterDialog.class, "ParameterDialog.jButton1.text")); // NOI18N
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(ParameterDialog.class, "ParameterDialog.okButton.text")); // NOI18N
        okButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                okButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout parametersPanelLayout = new javax.swing.GroupLayout(parametersPanel);
        parametersPanel.setLayout(parametersPanelLayout);
        parametersPanelLayout.setHorizontalGroup(
            parametersPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 0, Short.MAX_VALUE)
        );
        parametersPanelLayout.setVerticalGroup(
            parametersPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 256, Short.MAX_VALUE)
        );

        errorMessage.setFont(errorMessage.getFont().deriveFont((errorMessage.getFont().getStyle() | java.awt.Font.ITALIC)));
        errorMessage.setForeground(new java.awt.Color(255, 0, 0));
        org.openide.awt.Mnemonics.setLocalizedText(errorMessage, org.openide.util.NbBundle.getMessage(ParameterDialog.class, "ParameterDialog.errorMessage.text")); // NOI18N

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(parametersPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addComponent(errorMessage, javax.swing.GroupLayout.DEFAULT_SIZE, 261, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(okButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jButton1)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(parametersPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jButton1)
                    .addComponent(okButton)
                    .addComponent(errorMessage))
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
        canceled = true;
        setVisible(false);
    }//GEN-LAST:event_jButton1ActionPerformed

    private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
        canceled = true;
        setVisible(false);
    }//GEN-LAST:event_formWindowClosing

    private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
        boolean valid = true;
        for (int i = 0; i < parameters.size(); i++) {
            Parameter<?> parameter = parameters.get(i);
            ParameterType type = (ParameterType) combos.get(i).getSelectedItem();
            JLabel errorLabel = errorLabels.get(i);
            Editor editor = editorsByParameter.get(parameter);
            try {
                Object parameterValue = editor.getValue();
                type.setParameter(query, parameter, parameterValue);
                if (parameter.getName() != null) {
                    parameterCache.put(parameter.getName(), type, parameterValue);
                } else {
                    parameterCache.put(parameter.getPosition(), type, parameterValue);
                }
                editor.getComponent().setBackground(UIManager.getColor("TextField.background"));
                editor.getComponent().setToolTipText("");
                errorLabel.setText("");
            } catch (InvalidEditorValueException ex) {
                valid = false;
                editor.getComponent().setBackground(Color.PINK);
                editor.getComponent().setToolTipText("Invalid value for type.");
                errorLabel.setText("Invalid value for type.");
            } catch(IllegalArgumentException ex2) {
                valid = false;
                editor.getComponent().setToolTipText("The value can not be asigned to the parameter.");
                errorLabel.setText("The value can not be asigned to the parameter.");
                editor.getComponent().setBackground(Color.PINK);
            }
        }
        if (valid) {
            canceled = false;
            setVisible(false);
        }else{
            errorMessage.setText("There are errors in the captured values.");
        }
    }//GEN-LAST:event_okButtonActionPerformed

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(ParameterDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(ParameterDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(ParameterDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(ParameterDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the dialog */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                ParameterDialog dialog = new ParameterDialog(new javax.swing.JFrame(), true);
                dialog.addWindowListener(new java.awt.event.WindowAdapter() {
                    @Override
                    public void windowClosing(java.awt.event.WindowEvent e) {
                        System.exit(0);
                    }
                });
                dialog.setVisible(true);
            }
        });
    }

    public static boolean showParametersDialog(Component c, Query query, Set<Parameter<?>> parameters, ParameterCache parameterCache) {
        Frame frame = (Frame) SwingUtilities.getWindowAncestor(c);
        ParameterDialog dialog = new ParameterDialog(frame, true);
        dialog.setParameterCache(parameterCache);
        dialog.setQuery(query);
        dialog.setModal(true);
        dialog.setLocationRelativeTo(frame);
        dialog.setParameters(parameters);
        dialog.getRootPane().setDefaultButton(dialog.okButton);
        dialog.setVisible(true);
        return !dialog.canceled;
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel errorMessage;
    private javax.swing.JButton jButton1;
    private javax.swing.JButton okButton;
    private javax.swing.JPanel parametersPanel;
    // End of variables declaration//GEN-END:variables

}
